#if UNITY_EDITOR
using System.Collections.Generic;
using UnityEditor;
using UnityEngine;
using Object = UnityEngine.Object;

namespace GameDesigner
{
    internal static class Styles
    {
        public static readonly GUIStyle breadCrumbLeft = (GUIStyle)"GUIEditor.BreadcrumbLeft";
        public static readonly GUIStyle breadCrumbMid = (GUIStyle)"GUIEditor.BreadcrumbMid";
        public static readonly GUIStyle breadCrumbLeftBg = (GUIStyle)"GUIEditor.BreadcrumbLeftBackground";
        public static readonly GUIStyle breadCrumbMidBg = (GUIStyle)"GUIEditor.BreadcrumbMidBackground";

        public static readonly GUIContent layers = EditorGUIUtility.TrTextContent("Layers", "Click to edit controller's layers.");
        public static readonly GUIContent visibleON = EditorGUIUtility.IconContent("animationvisibilitytoggleon");
        public static readonly GUIContent visibleOFF = EditorGUIUtility.IconContent("animationvisibilitytoggleoff");
        public static readonly GUIStyle invisibleButton = "InvisibleButton";
    }

    internal class StateLayerEditor
    {
        internal string name;
        internal IStateMachine stateMachine;
    }

    internal enum SelectionDragMode
    {
        None,
        Rect,
        Pick
    }

    public partial class StateMachineWindow : GraphEditor
    {
        public static StateMachineWindow Singleton;
        public static StateMachineView support;
        public static IStateMachine stateMachine { get => support.editStateMachine; set => support.editStateMachine = (StateMachine)value; }
        private State makeTransition;
        internal static Transition selectTransition;
        private static readonly List<StateLayerEditor> layers = new();
        private SelectionDragMode isDraggingSelection;

        public StateMachineWindow()
        {
            Singleton = this;
        }

        [MenuItem("GameDesigner/StateMachine/StateMachine")]
        public static void ShowWindow()
        {
            Singleton = GetWindow<StateMachineWindow>(string.Empty, true);
            Singleton.titleContent = new GUIContent(PluginSetting.Language["Game Designer Editor Window"], EditorGUIUtility.IconContent("d_Animator Icon").image);
        }
        public static void ShowWindow(StateMachineView support)
        {
            ShowWindow();
            Init(support);
        }
        public static void Init(StateMachineView support)
        {
            StateMachineWindow.support = support;
            layers.Clear();
            if (support != null)
            {
                support.OnScriptReload();
                support.UpdateEditStateMachine(-1);
                layers.Add(new StateLayerEditor()
                {
                    name = stateMachine.name,
                    stateMachine = stateMachine,
                });
                if (Singleton != null)
                    Singleton.CenterView();
            }
        }

        protected override void OnEnable()
        {
            base.OnEnable();
            Init();
        }

        private void BreadCrumb(int index, string name)
        {
            var style = index == 0 ? Styles.breadCrumbLeft : Styles.breadCrumbMid;
            var guiStyle = index == 0 ? Styles.breadCrumbLeftBg : Styles.breadCrumbMidBg;
            var content = new GUIContent(name);
            var vector2 = style.CalcSize(content);
            var rect = GUILayoutUtility.GetRect(content, style, GUILayout.Height(20), GUILayout.MaxWidth(vector2.x));
            if (Event.current.type == EventType.Repaint)
                guiStyle.Draw(rect, GUIContent.none, 0);
            if (GUI.Button(rect, content, style))
            {
                stateMachine = layers[index].stateMachine;
                support.UpdateEditStateMachine(stateMachine.Id);
                layers.RemoveRange(index + 1, layers.Count - 1 - index);
            }
        }

        protected static bool IsRightClick() => Event.current.type == EventType.ContextClick;
        protected static bool IsDoubleClick() => Event.current.type == EventType.MouseDown && Event.current.button == 0 && Event.current.clickCount == 2;
        protected static bool IsLeftClick() => Event.current.type == EventType.MouseDown && Event.current.button == 0 && Event.current.clickCount == 1;

        protected void DrawStates()
        {
            foreach (var state in stateMachine.States)
            {
                if (state.position.Contains(mousePosition) & IsLeftClick())
                {
                    if (Event.current.control)
                        stateMachine.SelectState = state;
                    else if (!stateMachine.SelectStates.Contains(state.Id))
                        stateMachine.SelectStates = new List<int> { state.Id };
                    if (state.transitions.Length == 0)
                        selectTransition = null;
                    else
                        selectTransition = state.transitions[0];
                }
                else if (state.position.Contains(mousePosition) & IsDoubleClick())
                {
                    if (state.Type == StateType.SubStateMachine)
                    {
                        layers.Add(new StateLayerEditor()
                        {
                            name = state.name,
                            stateMachine = state.SubStateMachine,
                        });
                        stateMachine = state.SubStateMachine;
                        support.UpdateEditStateMachine(stateMachine.Id);
                        return;
                    }
                    else if (state.Type == StateType.Parent)
                    {
                        var index = layers.Count - 2;
                        if (index <= -1)
                        {
                            Debug.LogError("无法跳转，已经是根状态机!");
                            return;
                        }
                        stateMachine = layers[index].stateMachine;
                        support.UpdateEditStateMachine(stateMachine.Id);
                        layers.RemoveRange(index + 1, layers.Count - 1 - index);
                        return;
                    }
                }
                else if (state.position.Contains(mousePosition) & IsRightClick())
                {
                    stateMachine.SelectState = state;
                    var genericMenu = new GenericMenu();
                    genericMenu.AddItem(EditorGUIUtility.TrTextContent(PluginSetting.Language["Create transition"]), false, MakeTransitionCallback, state);
                    genericMenu.AddItem(EditorGUIUtility.TrTextContent(PluginSetting.Language["Default state"]), false, SetDefaultCallback, state);
                    genericMenu.AddItem(EditorGUIUtility.TrTextContent("Copy"), on: false, CopyStateCallback, state);
                    genericMenu.AddItem(EditorGUIUtility.TrTextContent(PluginSetting.Language["deleted state"]), false, DeleteStateCallback);
                    genericMenu.ShowAsContext();
                    Event.current.Use();
                }
                if (currentEvent.keyCode == KeyCode.Delete & currentEvent.type == EventType.KeyUp)
                {
                    DeleteStateCallback();
                    Event.current.Use();
                    return;
                }
            }
            foreach (var state in stateMachine.States)
                DrawStateBox(state);
        }

        private void MakeTransitionCallback(object userData)
        {
            makeTransition = userData as State;
        }

        private void SetDefaultCallback(object userData)
        {
            stateMachine.DefaultState = (State)userData;
            SetDirty(support);
        }

        private void CopyStateCallback(object userData)
        {
        }

        private void DeleteStateCallback()
        {
            var states = new List<State>();
            foreach (var i in stateMachine.SelectStates)
                states.Add(stateMachine.States[i]);
            stateMachine.RemoveStates(states.ToArray());
            stateMachine.UpdateStates();
            stateMachine.SelectStates.Clear();
            selectTransition = null;
            SetDirty(support);
        }

        private void DrawStateBox(State state)
        {
            var style = state.Type == StateType.None ? "node" : "node hex";
            var on = stateMachine.SelectStates.Contains(state.Id.Value) || (state.Id == stateMachine.StateId & Application.isPlaying);
            UnityEditor.Graphs.Styles.Color color;
            if (state == stateMachine.DefaultState)
                color = UnityEditor.Graphs.Styles.Color.Orange;
            else
                color = UnityEditor.Graphs.Styles.Color.Grey;
            if (stateMachine.StateId == state.Id && Application.isPlaying)
                color = UnityEditor.Graphs.Styles.Color.Aqua;
            var guiStyle = UnityEditor.Graphs.Styles.GetNodeStyle(style, color, on);
            GUI.Box(state.position, state.name, guiStyle);
        }

        public void DrawTransitionLines()
        {
            foreach (var state in stateMachine.States)
            {
                foreach (var transition in state.transitions)
                {
                    var color = Color.white;
                    if (selectTransition == transition)
                    {
                        color = Color.green;
                        if (Event.current.keyCode == KeyCode.Delete)
                        {
                            ArrayHelper.Remove(ref state.transitions, transition);
                            return;
                        }
                    }
                    DrawConnection(state.position.center, transition.NextState.position.center, color, 1, true);
                    ClickTransition(state, transition);
                }
            }
            if (makeTransition != null)
            {
                var startpos = new Vector2(makeTransition.position.x + 80, makeTransition.position.y + 15);
                var endpos = currentEvent.mousePosition;
                DrawConnection(startpos, endpos, Color.white, 1, true);
                if (currentEvent.button == 0 & currentType == EventType.MouseDown)
                {
                    foreach (var state in stateMachine.States)
                    {
                        if (makeTransition != state & state.position.Contains(mousePosition))
                        {
                            foreach (var transition in makeTransition.transitions)
                                if (transition.NextState == state)// 如果拖动的线包含在自身状态盒矩形内,则不添加连接线
                                    goto J;
                            Transition.CreateTransitionInstance(makeTransition, state);
                            stateMachine.SelectStates.Clear();
                            stateMachine.SelectState = state;
                            break;
                        }
                    }
                J:
                    makeTransition = null;
                    Event.current.Use();
                }
            }
        }

        protected void DragStates()
        {
            var current = Event.current;
            var controlID = GUIUtility.GetControlID(kDragNodesControlID, FocusType.Passive);
            switch (current.GetTypeForControl(controlID))
            {
                case EventType.MouseDown:
                    {
                        if (current.button == 0)
                        {
                            foreach (var stateId in stateMachine.SelectStates)
                            {
                                var state = stateMachine.States[stateId];
                                if (state.position.Contains(current.mousePosition))
                                {
                                    GUIUtility.hotControl = controlID;
                                    current.Use();
                                    return;
                                }
                            }
                            if (current.clickCount == 1)
                                stateMachine.SelectStates.Clear();
                        }
                    }
                    break;
                case EventType.MouseUp:
                    {
                        if (GUIUtility.hotControl == controlID)
                        {
                            SetDirty(support);
                            GUIUtility.hotControl = 0;
                            current.Use();
                        }
                    }
                    break;
                case EventType.MouseDrag:
                    {
                        if (GUIUtility.hotControl == controlID)
                        {
                            foreach (var state in stateMachine.SelectStates)
                                stateMachine.States[state].position.position += current.delta; //拖到状态按钮
                            SetDirty(support);
                            current.Use();
                        }
                    }
                    break;
                case EventType.KeyDown:
                    {
                        if (GUIUtility.hotControl == controlID && current.keyCode == KeyCode.Escape)
                        {
                            SetDirty(support);
                            GUIUtility.hotControl = 0;
                            current.Use();
                        }
                    }
                    break;
            }
        }

        protected void DragSelection()
        {
            var controlID = GUIUtility.GetControlID(kDragSelectionControlID, FocusType.Passive);
            var current = Event.current;
            switch (current.GetTypeForControl(controlID))
            {
                case EventType.MouseDown:
                    {
                        if (current.button == 0 && current.clickCount != 2 && !current.alt)
                        {
                            if (stateMachine.States.Length == 0)
                                return;
                            foreach (var state in stateMachine.States)
                                if (state.position.Contains(current.mousePosition))
                                    return;
                            GUIUtility.hotControl = controlID;
                            dragStartPoint = current.mousePosition;
                            isDraggingSelection = SelectionDragMode.None;
                            current.Use();
                        }
                    }
                    break;
                case EventType.MouseUp:
                    {
                        if (GUIUtility.hotControl == controlID)
                        {
                            GUIUtility.hotControl = 0;
                            isDraggingSelection = SelectionDragMode.None;
                            current.Use();
                        }
                    }
                    break;
                case EventType.MouseDrag:
                    {
                        if (GUIUtility.hotControl == controlID)
                        {
                            isDraggingSelection = SelectionDragMode.Rect;
                            SelectNodesInRect(FromToRect(dragStartPoint, current.mousePosition));
                            current.Use();
                        }
                    }
                    break;
                case EventType.KeyDown:
                    {
                        if (isDraggingSelection != SelectionDragMode.None && current.keyCode == KeyCode.Escape)
                        {
                            GUIUtility.hotControl = 0;
                            isDraggingSelection = SelectionDragMode.None;
                            current.Use();
                        }
                    }
                    break;
                case EventType.Repaint:
                    {
                        if (isDraggingSelection == SelectionDragMode.Rect)
                        {
                            DrawSelectionRect(FromToRect(dragStartPoint, current.mousePosition));
                        }
                    }
                    break;
            }
        }

        private void DrawSelectionRect(Rect sr)
        {
            var vector = graphUI.transform.matrix.MultiplyPoint(sr.min);
            var vector2 = graphUI.transform.matrix.MultiplyPoint(sr.max);
            selectionRect = Rect.MinMaxRect(vector.x, vector.y, vector2.x, vector2.y);
        }

        private void SelectNodesInRect(Rect r)
        {
            stateMachine.SelectStates.Clear();
            foreach (var state in stateMachine.States)
            {
                var position = state.position;
                if (position.xMax >= r.x && position.x <= r.xMax && position.yMax >= r.y && position.y <= r.yMax)
                {
                    stateMachine.SelectStates.Add(state.Id.Value);
                }
            }
        }

        private Rect FromToRect(Vector2 start, Vector2 end)
        {
            var rect = new Rect(start.x, start.y, end.x - start.x, end.y - start.y);
            if (rect.width < 0f)
            {
                rect.x += rect.width;
                rect.width = -rect.width;
            }
            if (rect.height < 0f)
            {
                rect.y += rect.height;
                rect.height = -rect.height;
            }
            return rect;
        }

        protected void ClickTransition(State state, Transition t)
        {
            if (state.position.Contains(mousePosition) || t.NextState.position.Contains(mousePosition))
                return;
            if (currentType == EventType.MouseDown)
            {
                var offset = state.Id > t.NextState.Id;
                Vector3 start = state.position.center;
                Vector3 end = t.NextState.position.center;
                Vector3 cross = Vector3.Cross((start - end).normalized, Vector3.forward);
                if (offset)
                {
                    start += cross * 6;
                    end += cross * 6;
                }
                if (HandleUtility.DistanceToLine(start, end) < 8f)//返回到线的距离
                {
                    selectTransition = t;
                    stateMachine.SelectState = state;
                }
            }
        }

        protected void SetDirty(Object target)
        {
            EditorUtility.SetDirty(target);
        }
    }
}
#endif